package com.boot2.core.dao.mongo;

import com.boot2.core.dao.exception.DAOException;
import com.github.pagehelper.Page;
import com.boot2.core.dao.DaoUtil;
import com.boot2.core.dao.PageInfo;
import com.boot2.core.dict.PersistentEnum;
import com.boot2.core.utils.ReflectUtil;
import com.boot2.core.utils.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author zhangweilin
 * @description:mongo 通用 mongo DAO
 * @date 2018/7/17
 */

public abstract class MongoDao<T> {
    public static final String FILEURL = "imgRespository";
    @Autowired
    protected MongoTemplate mongoTemplate;
    /**
     * 范型对像
     */
    protected Class<T> clazz;

    public MongoDao() {
        String string = getClass().getGenericInterfaces().toString();
        clazz = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 创建集合
     */
    public MongoCollection<Document> createCollection() {
        if (!this.mongoTemplate.collectionExists(clazz)) {
            MongoCollection<Document> dbCollection = this.mongoTemplate.createCollection(clazz);
            return dbCollection;
        }
        return null;
    }

    /**
     * 查找所有
     *
     * @return
     */
    public List<T> findAll() {
        return mongoTemplate.findAll(clazz);
    }

    /**
     * 查找所有
     *
     * @return
     */
    public <E> List<E> findAll(Class<E> clazz) {
        return mongoTemplate.findAll(clazz);
    }

    /**
     * 查找所有
     *
     * @return
     */
    public List<T> findAll(String collectionName) {
        return mongoTemplate.findAll(clazz, collectionName);
    }

    /**
     * 查找所有
     *
     * @return
     */
    public <E> List<E> findAll(Class<E> clazz, String collectionName) {
        return mongoTemplate.findAll(clazz, collectionName);
    }

    /**
     * 查找单个 只返回排序为第一的那条
     *
     * @param params
     * @return
     */
    public T findOne(Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个 只返回排序为第一的那条
     *
     * @param params
     * @return
     */
    public <E> E findOne(Class<E> clazz, Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个 只返回排序为第一的那条
     *
     * @param params
     * @return
     */
    public T findOne(String collectionName, Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个 只返回排序为第一的那条
     *
     * @param params
     * @return
     */
    public <E> E findOne(String collectionName, Class<E> clazz, Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个
     *
     * @param t
     * @return
     */
    public T findOne(T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个
     *
     * @param t
     * @return
     */
    public <E> E findOne(T t, Class<E> clazz, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个
     *
     * @param t
     * @return
     */
    public T findOne(String collectionName, T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个
     *
     * @param t
     * @return
     */
    public <E> E findOne(String collectionName, Class<E> clazz, T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public T findOneById(Serializable id) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public <E> E findOneById(Serializable id, Class<E> clazz) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public T findOneById(String collectionName, Serializable id) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public <E> E findOneById(String collectionName, Serializable id, Class<E> clazz) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        return mongoTemplate.findOne(query, clazz, collectionName);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public T findById(Object id) {
        return mongoTemplate.findById(id, clazz);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public <E> E findById(Object id, Class<E> clazz) {
        return mongoTemplate.findById(id, clazz);
    }

    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public T findById(String collectionName, Object id) {
        return mongoTemplate.findById(id, clazz, collectionName);
    }


    /**
     * 查找单个
     *
     * @param id
     * @return
     */
    public <E> E findById(String collectionName, Object id, Class<E> clazz) {
        return mongoTemplate.findById(id, clazz, collectionName);
    }


    /**
     * 添加单个
     *
     * @param t
     */
    public void insert(T t) {
        this.mongoTemplate.insert(t);
    }


    /**
     * 添加单个
     *
     * @param t
     */
    public void insert(String collectionName, T t) {
        this.mongoTemplate.insert(t, collectionName);
    }

    /**
     * 添加单个
     *
     * @param t
     */
    public void save(T t) {
        insert(t);
    }

    /**
     * 添加单个
     *
     * @param t
     */
    public void save(String collectionName, T t) {
        insert(collectionName, t);
    }

    /**
     * 批量添加
     *
     * @param collection
     * @param entityClass
     */
    public void insert(Collection<? extends T> collection, Class<?> entityClass) {
        this.mongoTemplate.insert(collection, entityClass);
    }

    /**
     * 批量添加
     *
     * @param collection
     * @param collectionName
     */
    public void insert(String collectionName, Collection<? extends T> collection) {
        this.mongoTemplate.insert(collection, collectionName);
    }

    /**
     * 批量添加
     *
     * @param collection
     * @param entityClass
     */
    public void save(Collection<? extends T> collection, Class<?> entityClass) {
        insert(collection, entityClass);
    }

    /**
     * 批量添加
     *
     * @param collection
     * @param collectionName
     */
    public void save(String collectionName, Collection<? extends T> collection) {
        insert(collectionName, collection);
    }

    /**
     * 批量添加
     *
     * @param collection
     */
    public void insertAll(Collection<? extends T> collection, String collectionName) {
        this.mongoTemplate.insert(collection, collectionName);
    }

    /**
     * 批量添加
     *
     * @param collection
     */
    public void insertAll(Collection<? extends T> collection) {
        this.mongoTemplate.insertAll(collection);
    }


    /**
     * 批量添加
     *
     * @param collection
     */
    public void saveAll(Collection<? extends T> collection) {
        insertAll(collection);
    }


    /**
     * 统计数量
     *
     * @param t
     * @return
     */
    public long count(Query query) {
        return mongoTemplate.count(query, clazz);
    }

    /**
     * 统计数量
     *
     * @param params
     * @return
     */
    public long count(Map<String, Object> params) {
        Query query = buildQuery(params);
        return mongoTemplate.count(query, clazz);
    }

    /**
     * 统计数量
     *
     * @param params
     * @return
     */
    public long count(String collectionName, Map<String, Object> params) {
        Query query = buildQuery(params);
        return mongoTemplate.count(query, clazz, collectionName);
    }

    /**
     * 统计数量
     *
     * @param t
     * @return
     */
    public long count(T t) {
        Query query = buildQuery(t);
        return mongoTemplate.count(query, clazz);
    }

    /**
     * 统计数量
     *
     * @param t
     * @return
     */
    public long count(String collectionName, T t) {
        Query query = buildQuery(t);
        return mongoTemplate.count(query, clazz, collectionName);
    }

    /**
     * 查找列表
     *
     * @param params
     * @param orderStr
     * @return
     */
    public List<T> findList(Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return findList(query);
    }


    /**
     * 查找列表
     *
     * @param params
     * @param orderStr
     * @return
     */
    public List<T> findList(String collectionName, Map<String, Object> params, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return findList(collectionName, query);
    }


    /**
     * 查找列表
     *
     * @param params
     * @param clazz
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(Map<String, Object> params, Class<E> clazz, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return findList(query, clazz);
    }

    /**
     * 查找列表
     *
     * @param params
     * @param clazz
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(String collectionName, Map<String, Object> params, Class<E> clazz, String... orderStr) {
        Query query = buildQuery(params);
        buildOrders(query, orderStr);
        return findList(collectionName, query, clazz);
    }

    /**
     * 查找列表
     *
     * @param t
     * @param orderStr
     * @return
     */
    public List<T> findList(T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return findList(query);
    }

    /**
     * 查找列表
     *
     * @param t
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(T t, Class<E> clazz, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return findList(query, clazz);
    }

    /**
     * 查找列表
     *
     * @param t
     * @param collectionName
     * @param orderStr
     * @return
     */
    public List<T> findList(String collectionName, T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return findList(collectionName, query);
    }

    /**
     * 查找列表
     *
     * @param t
     * @param collectionName
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(String collectionName, Class<E> clazz, T t, String... orderStr) {
        Query query = buildQuery(t);
        buildOrders(query, orderStr);
        return findList(collectionName, query, clazz);
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param query
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public List<T> findList(PageInfo<T> pageInfo, Query query, String... orderStr) {
        long count = count(query);
        List<T> list = findListByPage(pageInfo, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param params
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public List<T> findList(PageInfo<T> pageInfo, Map<String, Object> params, String... orderStr) {
        long count = count(params);
        Query query = buildQuery(params);
        List<T> list = findListByPage(pageInfo, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param params
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public <E> List<E> findList(PageInfo<E> pageInfo, Map<String, Object> params, Class<E> clazz, String... orderStr) {
        long count = count(params);
        Query query = buildQuery(params);
        List<E> list = findListByPage(pageInfo, clazz, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param params
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public List<T> findList(String collectionName, PageInfo<T> pageInfo, Map<String, Object> params, String... orderStr) {
        long count = count(params);
        Query query = buildQuery(params);
        List<T> list = findListByPage(collectionName, pageInfo, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param params
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public <E> List<E> findList(String collectionName, PageInfo<E> pageInfo, Map<String, Object> params, Class<E> clazz, String... orderStr) {
        long count = count(params);
        Query query = buildQuery(params);
        List<E> list = findListByPage(collectionName, clazz, pageInfo, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param t
     * @param orderStr 按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public List<T> findList(PageInfo<T> pageInfo, T t, String... orderStr) {
        long count = count(t);
        Query query = buildQuery(t);
        List<T> list = findListByPage(pageInfo, count, query, orderStr);
        return list;
    }

    /**
     * 分页查找列表
     *
     * @param pageInfo
     * @param t
     * @param collectionName
     * @param orderStr       按sql中的order by后面写即可，如 "createTime desc","price asc",其中asc可以不写
     * @return
     */
    public List<T> findList(String collectionName, PageInfo<T> pageInfo, T t, String... orderStr) {
        long count = count(t);
        Query query = buildQuery(t);
        List<T> list = findListByPage(collectionName, pageInfo, count, query, orderStr);
        return list;
    }


    /**
     * 查找列表
     *
     * @param query
     * @param orderStr
     * @return
     */
    public List<T> findList(Query query, String... orderStr) {
        buildOrders(query, orderStr);
        return this.mongoTemplate.find(query, clazz);
    }


    /**
     * 查找列表
     *
     * @param query
     * @param collectionName
     * @param orderStr
     * @return
     */
    public List<T> findList(String collectionName, Query query, String... orderStr) {
        buildOrders(query, orderStr);
        return this.mongoTemplate.find(query, clazz, collectionName);
    }


    /**
     * 查找列表
     *
     * @param query
     * @param clazz
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(Query query, Class<E> clazz, String... orderStr) {
        buildOrders(query, orderStr);
        return this.mongoTemplate.find(query, clazz);
    }

    /**
     * 查找列表
     *
     * @param query
     * @param clazz
     * @param collectionName
     * @param orderStr
     * @return
     */
    public <E> List<E> findList(String collectionName, Query query, Class<E> clazz, String... orderStr) {
        buildOrders(query, orderStr);
        return this.mongoTemplate.find(query, clazz, collectionName);
    }


    /**
     * 更新  第一条
     *
     * @param id
     * @param params
     */
    public UpdateResult updateOne(String id, Map<String, Object> params) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = new Update();
        if (!CollectionUtils.isEmpty(params)) {
            for (String key : params.keySet()) {
                update.set(key, params.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, clazz);
        return updateResult;
    }

    /**
     * 更新  第一条
     *
     * @param id
     * @param params
     */
    public UpdateResult updateOne(String collectionName, String id, Map<String, Object> params) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = new Update();
        if (!CollectionUtils.isEmpty(params)) {
            for (String key : params.keySet()) {
                update.set(key, params.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, clazz, collectionName);
        return updateResult;
    }

    /**
     * 更新  第一条
     *
     * @param id
     * @param t  此参数即可是查出来后修改，也可以是直接new的，即游离态的
     */
    public UpdateResult updateOne(String id, T t) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = buildUpdate(t);
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, clazz);
        return updateResult;
    }


    /**
     * 更新  第一条
     *
     * @param id
     * @param t  此参数即可是查出来后修改，也可以是直接new的，即游离态的
     */
    public UpdateResult updateOne(String collectionName, String id, T t) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = buildUpdate(t);
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, clazz, collectionName);
        return updateResult;
    }


    /**
     * map结构的更新  更新所有
     *
     * @param queryParam   map结构 查询参数可以是多条件
     * @param updateParams map结构  更新内容也可以是多内容
     */
    public UpdateResult updateMulti(Map<String, Object> queryParam, Map<String, Object> updateParams) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParams)) {
            for (String key : updateParams.keySet()) {
                update.set(key, updateParams.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.updateMulti(query, update, clazz);
        return updateResult;
    }

    /**
     * map结构的更新  更新所有
     *
     * @param queryParam   map结构 查询参数可以是多条件
     * @param updateParams map结构  更新内容也可以是多内容
     */
    public UpdateResult updateMulti(String collectionName, Map<String, Object> queryParam, Map<String, Object> updateParams) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParams)) {
            for (String key : updateParams.keySet()) {
                update.set(key, updateParams.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.updateMulti(query, update, clazz, collectionName);
        return updateResult;
    }

    /**
     * 实体对象结构的更新  更新所有
     *
     * @param queryT  实体对象结构 查询参数可以是多条件
     * @param updateT 实体对象结构  更新内容也可以是多内容
     */
    public UpdateResult updateMulti(T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.updateMulti(query, update, clazz);
        return updateResult;
    }

    /**
     * 实体对象结构的更新  更新所有
     *
     * @param queryT  实体对象结构 查询参数可以是多条件
     * @param updateT 实体对象结构  更新内容也可以是多内容
     */
    public UpdateResult updateMulti(String collectionName, T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.updateMulti(query, update, clazz, collectionName);
        return updateResult;
    }

    /**
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param queryParam  查询的条件
     * @param updateParam 更新条件
     */
    public UpdateResult upsert(Map<String, Object> queryParam, Map<String, Object> updateParam) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParam)) {
            for (String key : updateParam.keySet()) {
                update.set(key, updateParam.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz);
        return updateResult;
    }

    /**
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param queryParam  查询的条件
     * @param updateParam 更新条件
     */
    public UpdateResult upsert(String collectionName, Map<String, Object> queryParam, Map<String, Object> updateParam) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParam)) {
            for (String key : updateParam.keySet()) {
                update.set(key, updateParam.get(key));
            }
        }
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz, collectionName);
        return updateResult;
    }

    /**
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param queryT  查询的条件
     * @param updateT 更新条件
     */
    public UpdateResult upsert(T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz);
        return updateResult;
    }

    /**
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param queryT  查询的条件
     * @param updateT 更新条件
     */
    public UpdateResult upsert(String collectionName, T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz, collectionName);
        return updateResult;
    }

    /**
     * 按id upsert
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param id      查询的条件
     * @param updateT 更新条件
     */
    public UpdateResult upsert(Serializable id, T updateT) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz);
        return updateResult;
    }

    /**
     * 按id upsert
     * 先查询，如果没有符合条件的，会执行插入，插入的值是查询值 ＋ 更新值。
     * 如果有符合条件的，则直接更新该条记录
     *
     * @param id      查询的条件
     * @param updateT 更新条件
     */
    public UpdateResult upsert(String collectionName, Serializable id, T updateT) {
        Query query = new Query();
        query.addCriteria(new Criteria("_id").is(id));
        Update update = buildUpdate(updateT);
        UpdateResult updateResult = this.mongoTemplate.upsert(query, update, clazz, collectionName);
        return updateResult;
    }


    /**
     * 查找并修改，这个和update有区别，此带CAS作用，即具备原子操作，性能比update略差
     * 值得注意的是，它只对能找到的第一条起作用，
     *
     * @param queryParam  查询的条件
     * @param updateParam 更新条件
     */
    public T findAndModify(Map<String, Object> queryParam, Map<String, Object> updateParam) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParam)) {
            for (String key : updateParam.keySet()) {
                update.set(key, updateParam.get(key));
            }
        }
        T t = this.mongoTemplate.findAndModify(query, update, clazz);
        return t;
    }

    /**
     * 查找并修改，这个和update有区别，此带CAS作用，即具备原子操作，性能比update略差
     * 值得注意的是，它只对能找到的第一条起作用，
     *
     * @param queryParam  查询的条件
     * @param updateParam 更新条件
     */
    public T findAndModify(String collectionName, Map<String, Object> queryParam, Map<String, Object> updateParam) {
        Query query = buildQuery(queryParam);
        Update update = new Update();
        if (!CollectionUtils.isEmpty(updateParam)) {
            for (String key : updateParam.keySet()) {
                update.set(key, updateParam.get(key));
            }
        }
        T t = this.mongoTemplate.findAndModify(query, update, clazz, collectionName);
        return t;
    }


    /**
     * 查找并修改，这个和update有区别，此带CAS作用，即具备原子操作，性能比update略差
     * 值得注意的是，它只对能找到的第一条起作用，
     *
     * @param queryT  查询的条件
     * @param updateT 更新条件
     */
    public T findAndModify(T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        T t = this.mongoTemplate.findAndModify(query, update, clazz);
        return t;
    }

    /**
     * 查找并修改，这个和update有区别，此带CAS作用，即具备原子操作，性能比update略差
     * 值得注意的是，它只对能找到的第一条起作用，
     *
     * @param queryT  查询的条件
     * @param updateT 更新条件
     */
    public T findAndModify(String collectionName, T queryT, T updateT) {
        Query query = buildQuery(queryT);
        Update update = buildUpdate(updateT);
        T t = this.mongoTemplate.findAndModify(query, update, clazz, collectionName);
        return t;
    }

    /**
     * 查找并删除
     *
     * @param queryParam 查询的条件
     */
    public T findAndRemove(Map<String, Object> queryParam) {
        Query query = buildQuery(queryParam);
        T t = this.mongoTemplate.findAndRemove(query, clazz);
        return t;
    }

    /**
     * 查找并删除
     *
     * @param queryParam 查询的条件
     */
    public T findAndRemove(String collectionName, Map<String, Object> queryParam) {
        Query query = buildQuery(queryParam);
        T t = this.mongoTemplate.findAndRemove(query, clazz, collectionName);
        return t;
    }


    /**
     * 查找并删除
     *
     * @param queryT 查询的条件
     */
    public T findAndRemove(T queryT) {
        Query query = buildQuery(queryT);
        T t = this.mongoTemplate.findAndRemove(query, clazz);
        return t;
    }


    /**
     * 查找并删除
     *
     * @param queryT 查询的条件
     */
    public T findAndRemove(String collectionName, T queryT) {
        Query query = buildQuery(queryT);
        T t = this.mongoTemplate.findAndRemove(query, clazz, collectionName);
        return t;
    }


    /**
     * 按id删除
     *
     * @param id
     */
    public DeleteResult deleteById(String id) {
        DeleteResult deleteResult = mongoTemplate.remove(new Query(Criteria.where("_id").is(id)), clazz);
        return deleteResult;
    }

    /**
     * 按id删除
     *
     * @param id
     */
    public DeleteResult deleteById(String collectionName, String id) {
        DeleteResult deleteResult = mongoTemplate.remove(new Query(Criteria.where("_id").is(id)), clazz, collectionName);
        return deleteResult;
    }

//    /**
//     * 保存文件
//     *
//     * @param file
//     * @param fileUrl
//     */
//    public void saveFile(File file, String fileUrl) {
//        MongoDatabase db = mongoTemplate.getDb();
//        GridFS fs = new GridFS(db, FILEURL);
//        GridFSInputFile inputFile = null;
//        try {
//            inputFile = fs.createFile(file);
//        } catch (IOException e) {
////            e.printStackTrace();
//            throw new BusinessException("保存文件失败", e);
//        }
//        inputFile.setFilename(fileUrl);
//        inputFile.setContentType(fileUrl.substring(fileUrl.lastIndexOf(".")));
//        inputFile.save();
//    }

//    /**
//     * 读取文件
//     *
//     * @param filename
//     * @return
//     */
//    public GridFSDBFile getFileOne(String filename) {
//        try {
//            DB db = mongoTemplate.getDb();
//            // 获取fs的根节点
//            GridFS gridFS = new GridFS(db, FILEURL);
//            GridFSDBFile dbfile = gridFS.findOne(filename);
//            if (dbfile != null) {
//                return dbfile;
//            }
//        } catch (Exception e) {
////            e.printStackTrace();
//            throw new BusinessException("读取文件失败", e);
//        }
//        return null;
//    }

    /**
     * 数据合并，特别适用于数据同步,具备中途容错机制,使用案例如下
     * <pre>  //使用StationRealTimeEnergy中，stationId和logTime为联合唯一索引
     *      Date date = DateUtil.getFormatDate("2018-07-19 23:12:27", DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss);
     *         StationRealTimeEnergy stationRealTimeEnergy = new StationRealTimeEnergy();
     *         stationRealTimeEnergy.setPower(23.2);
     *         stationRealTimeEnergy.setBusVolt(234D);
     *         stationRealTimeEnergy.setFac(23D);
     *         stationRealTimeEnergy.setFac2(243D);
     *         stationRealTimeEnergy.setVpv2(23D);
     *         stationRealTimeEnergy.setVac3(25D);
     *         stationRealTimeEnergy.setVpv3(23D);
     *         stationRealTimeEnergy.setStationId("223456789654323");//电站id(逆变器sn)
     *         stationRealTimeEnergy.setLogTime(date);
     *
     *         //通过唯一索引，构建查询条件
     *         List<BathUpdateOptions> bathUpdateOptionsList = new ArrayList<>();
     *         StationRealTimeEnergy stationRealTimeEnergy2 = new StationRealTimeEnergy();
     *         stationRealTimeEnergy2.setStationId(stationRealTimeEnergy.getStationId());
     *         stationRealTimeEnergy2.setLogTime(stationRealTimeEnergy.getLogTime());
     *
     *         for (int i = 0; i < 10; i++) {
     *             BathUpdateOptions bathUpdateOptions = new BathUpdateOptions();
     *             //查询条件
     *             bathUpdateOptions.setQueryT(stationRealTimeEnergy2);
     *             //入库数据
     *             bathUpdateOptions.setUpdateT(stationRealTimeEnergy);
     *             //开启多条
     *             bathUpdateOptions.setIsMulti(true);
     *             //开启upsert,具体upsert，见dao中的upsert()方法,即通过查询条件，查询如果存在，则更新，如果不存在，则添加
     *             bathUpdateOptions.setIsUpsert(true);
     *             bathUpdateOptionsList.add(bathUpdateOptions);
     *         }
     *
     *         BathUpdateOptions bathUpdateOptions = new BathUpdateOptions();
     *         bathUpdateOptions.setQueryT(stationRealTimeEnergy2);
     *         //同一个唯一索引条件下，当有新的值时，此值会更新到数据库中去
     *         stationRealTimeEnergy.setPower(168D);
     *         bathUpdateOptions.setUpdateT(stationRealTimeEnergy);
     *         bathUpdateOptions.setIsMulti(true);
     *         bathUpdateOptions.setIsUpsert(true);
     *         bathUpdateOptionsList.add(bathUpdateOptions);
     *         //ordered=false表示出错忽略
     *         merge(bathUpdateOptionsList);
     * </pre>
     *
     * @param bathUpdateOptionsList
     * @return
     */
    public int merge(List<BathUpdateOptions<T>> bathUpdateOptionsList) {
        return doBathUpdate(bathUpdateOptionsList, false);
    }

    public int merge(String collectionName, List<BathUpdateOptions<T>> bathUpdateOptionsList) {
        return doBathUpdate(collectionName, bathUpdateOptionsList, false);
    }

    /**
     * @param bathUpdateOptionsList 要批量操作的数据
     * @param ordered               如果中间出错，当为false时，
     * @return
     */
    private int doBathUpdate(List<BathUpdateOptions<T>> bathUpdateOptionsList, boolean ordered) {
        BasicDBObject command = new BasicDBObject();
        command.put("update", determineCollectionName());
        List<BasicDBObject> updateList = new ArrayList<BasicDBObject>();
        for (BathUpdateOptions<T> option : bathUpdateOptionsList) {
            BasicDBObject update = new BasicDBObject();
            update.put("q", buildQuery(option.getQueryT()).getQueryObject());
            update.put("u", buildUpdate(option.getUpdateT()).getUpdateObject());
            update.put("upsert", option.getIsUpsert());
            update.put("multi", option.getIsMulti());
            updateList.add(update);
        }
        command.put("updates", updateList);
        command.put("ordered", ordered);

        Document commandResult = mongoTemplate.executeCommand(command.toJson());
        return Integer.parseInt(commandResult.get("n").toString());
    }

    /**
     * @param bathUpdateOptionsList 要批量操作的数据
     * @param ordered               如果中间出错，当为false时，
     * @return
     */
    private int doBathUpdate(String collectionName, List<BathUpdateOptions<T>> bathUpdateOptionsList, boolean ordered) {
        BasicDBObject command = new BasicDBObject();
        command.put("update", collectionName);
        List<BasicDBObject> updateList = new ArrayList<BasicDBObject>();
        for (BathUpdateOptions<T> option : bathUpdateOptionsList) {
            BasicDBObject update = new BasicDBObject();
            update.put("q", buildQuery(option.getQueryT()).getQueryObject());
            update.put("u", buildUpdate(option.getUpdateT()).getUpdateObject());
            update.put("upsert", option.getIsUpsert());
            update.put("multi", option.getIsMulti());
            updateList.add(update);
        }
        command.put("updates", updateList);
        command.put("ordered", ordered);
        Document commandResult = mongoTemplate.executeCommand(command.toJson());
        return Integer.parseInt(commandResult.get("n").toString());
    }


    public MongoTemplate getMongoTemplate() {
        return mongoTemplate;
    }

    /**
     * 获取集合
     *
     * @return
     */
    private MongoCollection<Document> getDBCollection() {
        MongoCollection<Document> collection = mongoTemplate.getCollection(determineCollectionName());
        return collection;
    }

    /**
     * 通过泛型获取集合名称
     *
     * @return
     */
    private String determineCollectionName() {
        String collectionName = mongoTemplate.getCollectionName(clazz);
        return collectionName;
    }

    /**
     * 通过泛型获取集合名称
     * @return
     */
//    private String determineCollectionName() {
//        MongoPersistentEntity<?> persistentEntity = mongoTemplate.getConverter().getMappingContext().getPersistentEntity(clazz);
//        if (persistentEntity == null) {
//            throw new InvalidDataAccessApiUsageException(
//                    "No Persistent Entity information found for the class " + clazz.getName());
//        }
//        return persistentEntity.getCollection();
//    }

    /**
     * 构建query对象
     *
     * @param params
     * @return
     */
    private Query buildQuery(Map<String, Object> params) {
        Query query = (Query) DaoUtil.buildCriteria(PersistentEnum.mongo, params);
        return query;
    }

    /**
     * 构建query对象
     *
     * @param t
     * @return
     */
    private Query buildQuery(T t) {
        Query query = (Query) DaoUtil.buildCriteria(PersistentEnum.mongo, t);
        return query;
    }

    /**
     * 执行分页查询
     *
     * @param pageInfo
     * @param count
     * @param query
     * @param orderStr
     * @return
     */
    public List<T> findListByPage(PageInfo<T> pageInfo, long count, Query query, String[] orderStr) {
        int pageNo = pageInfo.getPageNo();
        int pageSize = pageInfo.getPageSize();
        int skip = (pageNo - 1) * pageSize;
        int endRow = skip + pageSize;
        query.skip(skip).limit(pageSize);
        List<T> list = findList(query, orderStr);
        Page<T> page = new Page<>();
        page.addAll(list);
        page.setTotal(count);
        page.setStartRow(skip);
        page.setEndRow(endRow);
        page.setPageNum(pageNo);
        Long pageTotal = count / pageSize;
        pageTotal = count % pageSize == 0 ? pageTotal : pageTotal + 1;
        page.setPages(pageTotal.intValue());
        page.setPageSize(pageSize);
        page.setPageSizeZero(true);
        pageInfo.setResultList(page);
        return list;
    }

    /**
     * 执行分页查询
     *
     * @param pageInfo
     * @param count
     * @param query
     * @param orderStr
     * @return
     */
    public <E> List<E> findListByPage(PageInfo<E> pageInfo, Class<E> clazz, long count, Query query, String[] orderStr) {
        int pageNo = pageInfo.getPageNo();
        int pageSize = pageInfo.getPageSize();
        buildOrders(query, orderStr);
        int skip = (pageNo - 1) * pageSize;
        int endRow = skip + pageSize;
        query.skip(skip).limit(pageSize);
        List<E> list = findList(query, clazz);
        Page<E> page = new Page<>();
        page.addAll(list);
        page.setTotal(count);
        page.setStartRow(skip + 1);
        page.setEndRow(endRow);
        page.setPageNum(pageNo);
        Long pageTotal = count / pageSize;
        pageTotal = count % pageSize == 0 ? pageTotal : pageTotal + 1;
        page.setPages(pageTotal.intValue());
        page.setPageSize(pageSize);
        page.setPageSizeZero(true);
        pageInfo.setResultList(page);
        return list;
    }

    /**
     * 执行分页查询
     *
     * @param pageInfo
     * @param count
     * @param query
     * @param orderStr
     * @return
     */
    public List<T> findListByPage(String collectionName, PageInfo<T> pageInfo, long count, Query query, String[] orderStr) {
        int pageNo = pageInfo.getPageNo();
        int pageSize = pageInfo.getPageSize();
        buildOrders(query, orderStr);
        int skip = (pageNo - 1) * pageSize;
        int endRow = skip + pageSize;
        query.skip(skip).limit(pageSize);
        List<T> list = findList(query, collectionName);
        Page<T> page = new Page<>();
        page.addAll(list);
        page.setTotal(count);
        page.setStartRow(skip + 1);
        page.setEndRow(endRow);
        page.setPageNum(pageNo);
        Long pageTotal = count / pageSize;
        pageTotal = count % pageSize == 0 ? pageTotal : pageTotal + 1;
        page.setPages(pageTotal.intValue());
        page.setPageSize(pageSize);
        page.setPageSizeZero(true);
        pageInfo.setResultList(page);
        return list;
    }

    /**
     * 执行分页查询
     *
     * @param pageInfo
     * @param count
     * @param query
     * @param orderStr
     * @return
     */
    public <E> List<E> findListByPage(String collectionName, Class<E> clazz, PageInfo<E> pageInfo, long count, Query query, String[] orderStr) {
        int pageNo = pageInfo.getPageNo();
        int pageSize = pageInfo.getPageSize();
        buildOrders(query, orderStr);
        int skip = (pageNo - 1) * pageSize;
        int endRow = skip + pageSize;
        query.skip(skip).limit(pageSize);
        List<E> list = findList(query, clazz, collectionName);
        Page<E> page = new Page<>();
        page.addAll(list);
        page.setTotal(count);
        page.setStartRow(skip + 1);
        page.setEndRow(endRow);
        page.setPageNum(pageNo);
        Long pageTotal = count / pageSize;
        pageTotal = count % pageSize == 0 ? pageTotal : pageTotal + 1;
        page.setPages(pageTotal.intValue());
        page.setPageSize(pageSize);
        page.setPageSizeZero(true);
        pageInfo.setResultList(page);
        return list;
    }

    /**
     * 构建排序
     *
     * @param query
     * @param orderStr
     */
    private void buildOrders(Query query, String[] orderStr) {
        List<Sort.Order> sortOrderList = new ArrayList<>();
        if (null != orderStr) {
            for (int i = 0; i < orderStr.length; i++) {
                String order = orderStr[i];
                order = order.trim();
                if (order.endsWith(" DESC") || order.endsWith(" desc")) {
                    String orderName = order.replaceAll(" DESC| desc", "").trim();
                    sortOrderList.add(new Sort.Order(Sort.Direction.DESC, orderName));
                } else {
                    String orderName = order.replaceAll(" ASC| asc", "").trim();
                    sortOrderList.add(new Sort.Order(Sort.Direction.ASC, orderName));
                }
            }
        }
        if (!CollectionUtils.isEmpty(sortOrderList)) {
//            query.with(new Sort(sortOrderList));
            query.with( Sort.by(sortOrderList));
        }
    }

    /**
     * 构建更新对象
     *
     * @param t
     * @return
     */
    private Update buildUpdate(T t) {
        Update update = new Update();
        if (null != t) {
            List<Field> fieldList = ReflectUtil.getAllFieldWithParentNotTransientList(t.getClass(), "isDeleted");
            for (int i = 0; i < fieldList.size(); i++) {
                Field field = fieldList.get(i);
                try {
                    String fieldName = field.getName();
                    Object value = MethodUtils.invokeExactMethod(t, "get" + StringUtils.changFirstWord(fieldName, StringUtils.toUpperCase));
                    if (null != value) {
                        update.set(fieldName, value);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new DAOException("更新失败", e);
                }
            }
        }
        return update;
    }
}